#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "../src/model.c"

static bool
fuzzy_equal (float a,
             float b)
{
  return (fabs (a - b) <= 1e-5 * (fabs (a) + fabs (b)));
}

static bool
fuzzy_equal_array (const float* a,
                   const float* b,
                   size_t n)
{
  for (unsigned int i = 0; i < n; ++i)
    {
      if (! fuzzy_equal (a[i], b[i]))
        {
          return false;
        }
    }

  return true;
}

static void
test_model_once ()
{
  const char* pathname = tmpnam (NULL);
  unsigned int n_items = 1.0 + 50.0 * drand48 ();
  unsigned int n_labels = 1.0 + 50.0 * drand48 ();
  unsigned int n_workers = 1.0 + 50.0 * drand48 ();
  float logpriorz[n_labels];
  float alpha[n_workers * n_labels * n_labels];
  float gamma[n_labels * n_labels];
  bool symmetric = drand48 () < 0.5;

  for (unsigned int i = 0; i < n_labels; ++i)
    {
      logpriorz[i] = drand48 ();
    }

  memset (alpha, 0, sizeof (alpha));
  memset (gamma, 0, sizeof (gamma));

  Model* model = model_creat (pathname,
                              n_items,
                              n_labels,
                              n_workers,
                              logpriorz,
                              symmetric);

  assert (model->n_items == n_items);
  assert (model->n_labels == n_labels);
  assert (model->n_workers == n_workers);
  assert (fuzzy_equal_array (model->logpriorz, logpriorz, n_labels));
  assert (memcmp (model->alpha,
                  alpha,
                  n_workers * n_labels * n_labels * sizeof (float)) == 0);
  assert (memcmp (model->gamma,
                  gamma,
                  n_labels * n_labels * sizeof (float)) == 0);
  assert (model->symmetric == symmetric);

  for (unsigned int i = 0; i < n_workers * n_labels * n_labels; ++i)
    {
      model->alpha[i] = alpha[i] = drand48 ();
    }

  for (unsigned int i = 0; i < n_labels * n_labels; ++i)
    {
      model->gamma[i] = gamma[i] = drand48 ();
    }

  model_close (model);

  model = model_open (pathname, true);

  assert (model->n_items == n_items);
  assert (model->n_labels == n_labels);
  assert (model->n_workers == n_workers);
  assert (fuzzy_equal_array (model->logpriorz, logpriorz, n_labels));
  assert (memcmp (model->alpha,
                  alpha,
                  n_workers * n_labels * n_labels * sizeof (float)) == 0);
  assert (memcmp (model->gamma,
                  gamma,
                  n_labels * n_labels * sizeof (float)) == 0);
  assert (model->symmetric == symmetric);

  model_close (model);
  unlink (pathname);
}

static void
test_model (void)
{
  for (unsigned int i = 0; i < 100; ++i)
    {
      test_model_once ();
    }
}

int 
main (void) 
{
  srand48 (69);

  test_model ();

  return 0;
}
